package fr.upmc.i3s.fuglmeyeranalyser.tools.xls;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.LinkedList;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

import fr.upmc.i3s.fuglmeyeranalyser.patients.FMScore;
import fr.upmc.i3s.fuglmeyeranalyser.patients.Patient;
import fr.upmc.i3s.fuglmeyeranalyser.patients.PatientList;
import fr.upmc.i3s.fuglmeyeranalyser.patients.PrivatePatient;
import fr.upmc.i3s.fuglmeyeranalyser.patients.PrivatePatientList;

public class XLSParser implements XLSConstants {
	
	
	  /////////////////////////
	 // PUBLIC PATIENT FILE //
	/////////////////////////
	
	public static XLSDataFileContent getPatientListFromXLSFile(String xlsFile) throws IOException {
		FileInputStream file = new FileInputStream(new File(xlsFile));
		//Get the workbook instance for XLS file
		HSSFWorkbook workbook = new HSSFWorkbook(file);
		//Get first sheet from the workbook
		HSSFSheet sheet = workbook.getSheetAt(0);
		
		LinkedList<String> params = new LinkedList<String>();
		PatientList listPatient = new PatientList();
		
		//Iterate in current sheet
		for(Row row : sheet) {
			//Get row with parameters labels
			if(row.getRowNum() == 6) {
				params = getParamsFromRow(row);
			}
			
			//Get rows with values 
			final int first = 10;
			int exclude = 21;
			if(row.getRowNum() > first) { 
				if(row.getRowNum()!=exclude) {
					Patient p = getPatientFromRow(row, params);
					listPatient.add(p);
				} else {
					exclude+=11;
				}
			}
		}
		
		//Close stream
		file.close();
		
		//Return file content
		return new XLSDataFileContent(params, listPatient);
	}
	
	/**
	 * Get patient from row
	 */
	private static Patient getPatientFromRow(Row row, LinkedList<String> paramsModel) {
		Patient patient = new Patient();
		// get patient parameters values
		int i = 0;
		int cellnum = 0;
		for(Cell cell : row) {
			cellnum++; //increment for each cell
			try { 
				String value = getStringFromCell(cell);
				patient.addParam(paramsModel.get(i), value);
				i++; //increment only if usefull value
			} catch (UselessContentException e) { /* useless content */ }
			if(i>=paramsModel.size()) { break; }
		}
		
		// get patient DELAY-FM couples 
		for(i=cellnum; i<row.getLastCellNum(); i+=2) {
			try {
				double delay = getDoubleFromCell(row.getCell(i));
				double score = getDoubleFromCell(row.getCell(i+1));
				patient.addFMScore(new FMScore(delay, score));
			} catch (NotNumericCellException e) {
				// nothing to do, this score is N/A
			}
		}
		return patient;
	}
	
	
	  /////////////////////////
	 // KERNEL PATIENT LIST //
	/////////////////////////
	
	public static XLSKernelFileContent getKernelPatientListFromXLSFile(String xlsFile) throws IOException {
		FileInputStream file = new FileInputStream(new File(xlsFile));
		//Get the workbook instance for XLS file
		HSSFWorkbook workbook = new HSSFWorkbook(file);
		//Get first sheet from the workbook
		HSSFSheet sheet = workbook.getSheetAt(0);
		
		LinkedList<String> params = new LinkedList<String>();
		PrivatePatientList listPatient = new PrivatePatientList();
		
		//Iterate in current sheet
		for(Row row : sheet) {
			//Get row with parameters labels
			if(row.getRowNum() == 6) {
				params = getParamsFromRow(row);
			}
			
			//Get rows with values 
			final int first = 10;
			int exclude = 21;
			if(row.getRowNum() > first) { 
				if(row.getRowNum()!=exclude) {
					PrivatePatient p = getPrivatePatientFromRow(row, params);
					listPatient.add(p);
				} else {
					exclude+=11;
				}
			}
		}
		
		//Close stream
		file.close();
		
		//Return file content
		return new XLSKernelFileContent(params, listPatient);
	}
	
	
	/**
	 * Get private patient from row
	 */
	private static PrivatePatient getPrivatePatientFromRow(Row row, LinkedList<String> paramsModel) {
		PrivatePatient patient = new PrivatePatient();
		// get patient parameters values
		int i = 0;
		for(Cell cell : row) {
			try { 
				String value = getStringFromCell(cell);
				patient.addParam(paramsModel.get(i), value);
				i++; //increment only if usefull value
			} catch (UselessContentException e) { /* useless content */ }
			if(i>=paramsModel.size()) { break; }
		}
		return patient;
	}
	
	
	  ///////////////
	 // XLS UTILS //
	///////////////
	
	/**
	 * Get parameters model (parameters names).
	 */
	private static LinkedList<String> getParamsFromRow(Row row) {
		LinkedList<String> paramsModel = new LinkedList<String>();
		for(Cell cell : row) {
			try {
				String param = getStringFromCell(cell);
				if(!param.equals(XLS_NOT_AVAILABLE) && 
						!param.equals(XLS_FM_SCORE) &&
						!param.equals(XLS_DELAY)) {
					paramsModel.add(param);
				}
			} catch (UselessContentException e) {
				// Useless content
			}
		}
		return paramsModel;
	}
	
	 /////////////////
	 // CELL PARSER //
	/////////////////
	
	private static String getStringFromCell(Cell cell) throws UselessContentException {
		if(cell == null) {
			throw new UselessContentException();
		}
		String result = null;
		switch(cell.getCellType()) {
			case Cell.CELL_TYPE_STRING:
				result = cell.getStringCellValue();
				break;
			case Cell.CELL_TYPE_NUMERIC:
				result = String.valueOf(cell.getNumericCellValue());
				break;
			case Cell.CELL_TYPE_BOOLEAN:
			case Cell.CELL_TYPE_ERROR:
			case Cell.CELL_TYPE_FORMULA:
			case Cell.CELL_TYPE_BLANK:
			default:
				throw new UselessContentException();
		}
		return result;
	}
	
	private static double getDoubleFromCell(Cell cell) throws NotNumericCellException {
		if(cell == null) {
			throw new NotNumericCellException();
		}
		switch(cell.getCellType()) {
			case Cell.CELL_TYPE_NUMERIC:
				return cell.getNumericCellValue();
			default:
				throw new NotNumericCellException();
		}
	}
	
	
	  ////////////////
	 // EXCEPTIONS //
	////////////////
	
	private static class UselessContentException extends Exception {
		private static final long serialVersionUID = -5389354893036451840L;
		public UselessContentException() {
			super("This cell does not contain usefull value");
		}
	}
	
	private static class NotNumericCellException extends Exception {
		private static final long serialVersionUID = -5389332183036451840L;
		public NotNumericCellException() {
			super("This cell does not contain numeric value");
		}
	}
	
}
